/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/pointer"

	operatorerrors "k8s.io/kubeadm/operator/errors"
)

// RuntimeTaskGroupSpec defines the RuntimeTask template that applies to a group of nodes that should
// be changed as part of an Operation.
// Please note that this is a runtime object, create with the goal to allow ensure orchestration of
// operation RuntimeTasks/Commands, and that this object will be deleted immediately after the operation completes.
// Users can refer to this object only in case of errors/for problem investigation.
type RuntimeTaskGroupSpec struct {
	// NodeSelector is a label query that identifies the list of nodes that should be impacted by this RuntimeTaskGroup.
	// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
	// +optional
	NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty"`

	// NodeFilter allows to filter the list of nodes returned by the node selector.
	// It defaults to all.
	// +optional
	NodeFilter string `json:"nodeFilter,omitempty"`

	// Selector is a label query over RuntimeTasks that are generated by this RuntimeTaskGroup.
	// Label keys and values that must match in order to be controlled by this RuntimeTaskGroup.
	// It must match the RuntimeTask template's labels.
	// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
	// +optional
	Selector metav1.LabelSelector `json:"selector,omitempty"`

	// Template is the object that describes the RuntimeTask that will be created if
	// insufficient replicas are detected.
	// +optional
	Template RuntimeTaskTemplateSpec `json:"template,omitempty"`

	// CreateStrategy indicates how the controller should handle RuntimeTask creation for this RuntimeTaskGroup.
	// If missing, sequential mode will be used.
	// +optional
	CreateStrategy string `json:"createStrategy,omitempty"`
}

// RuntimeTaskTemplateSpec defines the RuntimeTask that applies to a group of nodes that should
// be changed as part of an Operation.
type RuntimeTaskTemplateSpec struct {
	// Standard object's metadata.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// Specification of the desired behavior of the RuntimeTask.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
	// +optional
	Spec RuntimeTaskSpec `json:"spec,omitempty"`
}

// RuntimeTaskGroupStatus defines the observed state of RuntimeTaskGroup
type RuntimeTaskGroupStatus struct {

	// StartTime represents time when the RuntimeTaskGroup execution was started by the controller.
	// It is represented in RFC3339 form and is in UTC.
	// +optional
	StartTime *metav1.Time `json:"startTime,omitempty"`

	// Paused indicates that the RuntimeTaskGroup is currently paused.
	// This fields is set when the OperationSpec.Paused value is processed by the controller.
	// +optional
	Paused bool `json:"paused,omitempty"`

	// CompletionTime represents time when the RuntimeTaskGroup was completed.
	// It is represented in RFC3339 form and is in UTC.
	// +optional
	CompletionTime *metav1.Time `json:"completionTime,omitempty"`

	// Nodes
	// +optional
	Nodes int32 `json:"nodes,omitempty"`

	// RunningNodes
	// +optional
	RunningNodes int32 `json:"runningNodes,omitempty"`

	// SucceededNodes
	// +optional
	SucceededNodes int32 `json:"succeededNodes,omitempty"`

	// FailedNodes
	// +optional
	FailedNodes int32 `json:"failedNodes,omitempty"`

	// InvalidNodes
	// +optional
	InvalidNodes int32 `json:"invalidNodes,omitempty"`

	// Phase represents the current phase of RuntimeTaskGroup actuation.
	// E.g. pending, running, succeeded, failed etc.
	// +optional
	Phase string `json:"phase,omitempty"`

	// ErrorReason will be set in the event that there is a problem in executing
	// one of the RuntimeTaskGroup's RuntimeTasks and will contain a succinct value suitable
	// for machine interpretation.
	// +optional
	ErrorReason *operatorerrors.RuntimeTaskGroupStatusError `json:"errorReason,omitempty"`

	// ErrorMessage will be set in the event that there is a problem in executing
	// one of the RuntimeTaskGroup's RuntimeTasks and will contain a more verbose string suitable
	// for logging and human consumption.
	// +optional
	ErrorMessage *string `json:"errorMessage,omitempty"`
}

// SetStartTime is a utility method for setting the StartTime field to Now.
func (s *RuntimeTaskGroupStatus) SetStartTime() {
	now := metav1.Now()
	s.StartTime = &now
}

// SetCompletionTime is a utility method for setting the CompletionTime field to Now.
func (s *RuntimeTaskGroupStatus) SetCompletionTime() {
	now := metav1.Now()
	s.CompletionTime = &now

	// ensure all the status attributes are clean after complete
	s.Paused = false
	s.ErrorReason = nil
	s.ErrorMessage = nil
}

// SetError is a utility method for setting the ErrorReason and ErrorMessage fields
// given a RuntimeTaskGroupError object.
func (s *RuntimeTaskGroupStatus) SetError(err *operatorerrors.RuntimeTaskGroupError) {
	reason := err.Reason
	s.ErrorReason = &reason
	s.ErrorMessage = pointer.StringPtr(err.Message)
}

// ResetError is a utility method for resetting the ErrorReason and ErrorMessage fields
func (s *RuntimeTaskGroupStatus) ResetError() {
	s.ErrorReason = nil
	s.ErrorMessage = nil
}

// +kubebuilder:resource:path=runtimetaskgroups,scope=Cluster,categories=kubeadm-operator
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:object:root=true
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase"
// +kubebuilder:printcolumn:name="Nodes",type="integer",JSONPath=".status.nodes"
// +kubebuilder:printcolumn:name="Succeeded",type="integer",JSONPath=".status.succeededNodes"
// +kubebuilder:printcolumn:name="Failed",type="integer",JSONPath=".status.failedNodes"

// RuntimeTaskGroup is the Schema for the runtimetaskgroups API
type RuntimeTaskGroup struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   RuntimeTaskGroupSpec   `json:"spec,omitempty"`
	Status RuntimeTaskGroupStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// RuntimeTaskGroupList contains a list of RuntimeTaskGroup
type RuntimeTaskGroupList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []RuntimeTaskGroup `json:"items"`
}

func init() {
	SchemeBuilder.Register(&RuntimeTaskGroup{}, &RuntimeTaskGroupList{})
}
